package com.cskaoyan.mall.cart.service.impl;

import com.cskaoyan.mall.cart.api.dto.CartInfoDTO;
import com.cskaoyan.mall.cart.client.ProductApiClient;
import com.cskaoyan.mall.cart.converter.SkuInfoConverter;
import com.cskaoyan.mall.cart.service.CartService;
import com.cskaoyan.mall.product.dto.SkuInfoDTO;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Author Pudding
 * @Date 2024/7/15 10:25
 */
@Service
public class CartServiceImpl implements CartService {

    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private ProductApiClient productApiClient;
    @Autowired
    private SkuInfoConverter skuInfoConverter;

    // 添加商品到购物车 用户Id，商品Id，商品数量
    @Override
    public void addToCart(Long skuId, String userId, Integer skuNum) {

        //0. 获取用户购物车的key，这样做的好处是为了让redis存储的数据见名知意
        String key = buildKey(userId);

        //1. 获取用户购物车
        RMap<Long, CartInfoDTO> userCartMap = redissonClient.getMap(key);

        //2. 查看用户购物车中是否包含该商品
        if (userCartMap.containsKey(skuId)) {
            //2.1 如果包含，更新数量（根据skuId，获取购物车中的CartInfoDTO, 修改数量）
            CartInfoDTO cartInfoDTO = userCartMap.get(skuId);
            cartInfoDTO.setSkuNum(skuNum + cartInfoDTO.getSkuNum());
            //2.2 更新时间
            cartInfoDTO.setUpdateTime(new Date());
            //2.3 更新选中状态
            cartInfoDTO.setIsChecked(1);

        } else {
            //2.2 如果不包含，添加商品到购物车(根据skuId调用商品服务(skuName, skuPrice, defaultImg) + skuNum ——> CartInfoDTO)
            //2.2.1 远程调用商品服务的接口，获取商品信息
            SkuInfoDTO skuInfo = productApiClient.getSkuInfo(skuId);

            //2.2.2 通过转换器将skuInfoDTO转换为CartInfoDTO
            CartInfoDTO cartInfoDTO = skuInfoConverter.skuInfoToCartInfo(skuInfo, skuNum, skuId, userId);

            //2.2.3 添加商品到购物车
            userCartMap.put(skuId, cartInfoDTO);
        }

    }

    //获取用户购物车中的key
    private String buildKey(String userId) {
        return "user:cart:" + userId;
    }

    /**
     * 通过用户Id和临时用户id  查询购物车列表
     */
    @Override
    public List<CartInfoDTO> getCartList(String userId, String userTempId) {

        // 1. 查询出已经登录的购物车、查询出未登录的购物车
        //1.1 获取存储在redis中的key
        String userCartKey = buildKey(userId);
        String userTempCartKey = buildKey(userTempId);

        //1.2 获取redis中的hash数据结构的数据
        RMap<Long, CartInfoDTO> userMap = redissonClient.getMap(userCartKey);
        RMap<Long, CartInfoDTO> userTempMap = redissonClient.getMap(userTempCartKey);

        //1.3 获取购物车中的数据
        List<CartInfoDTO> userValueList = userMap.values().stream().collect(Collectors.toList());
        List<CartInfoDTO> userTempValueList = userTempMap.values().stream().collect(Collectors.toList());

        // 2. 如果userId为空，那么表示用户未登录，直接返回用户userTempId的临时购物车列表
        if (StringUtils.isBlank(userId)) {
            return userTempValueList;
        }

        // 3. 如果userId不为空，那么表示用户已经登录
        // 3.1 如果未登录的购物车为空，那么直接返回已经登录的购物车
        if (CollectionUtils.isEmpty(userTempValueList)) {
            return userValueList;
        }

        // 3.2 如果未登录的购物车不为空，那么需要合并 未登录和已登录的购物车，得到合并之后的购物车
        // 合并需要注意 同一个商品的数量和状态(合并之后的数据，放到用户登录之后的购物车)
        List<CartInfoDTO> cartList = mergeCart(userValueList, userTempValueList);

        // 3.3 把合并之后的购物车的商品进行排序，按照添加购物车的时间排序
        List<CartInfoDTO> sortedList = cartList.stream().sorted((o1, o2) -> {
            return o1.getCreateTime().compareTo(o2.getCreateTime());
        }).collect(Collectors.toList());

        // 3.4 更新用户userId的购物车
        //3.4.1 获取用户购物车的map
        RMap<Long, CartInfoDTO> map = redissonClient.getMap(userCartKey);
        map.putAll(cartList.stream().collect(Collectors.toMap(CartInfoDTO::getSkuId,
                new Function<CartInfoDTO, CartInfoDTO>() {
                    @Override
                    public CartInfoDTO apply(CartInfoDTO cartInfoDTO) {
                        return cartInfoDTO;
                    }
                })));

        // 3.5 删除用户临时Id userTempId的购物车
        redissonClient.getMap(userTempCartKey).clear();

        // 3.6 返回合并之后的购物车
        return sortedList;
    }

    //将临时用户和普通用户的购物车合并
    private List<CartInfoDTO> mergeCart(List<CartInfoDTO> userValueList, List<CartInfoDTO> userTempValueList) {

        // 1. 把两个集合添加到一个大的集合中
        ArrayList<CartInfoDTO> cartInfoDTOS = new ArrayList<>();
        cartInfoDTOS.addAll(userValueList);
        cartInfoDTOS.addAll(userTempValueList);

        // 3. 使用一个map来接收集合中的每一个商品
        HashMap<Long, CartInfoDTO> map = new HashMap<>();

        // 2. 循环这个大的集合
        for (CartInfoDTO cartInfoDTO : cartInfoDTOS) {
            // 2.1 获取当前商品在map中的key
            Long skuId = cartInfoDTO.getSkuId();

            // 3.1 接收的时候判断当前map中有没有这个商品
            if (map.containsKey(skuId)) { //有，更新数量 | 更新选中状态

                CartInfoDTO existCartInfo = map.get(skuId);
                //更新数量
                existCartInfo.setSkuNum(existCartInfo.getSkuNum() + cartInfoDTO.getSkuNum());
                //更新选中状态
                existCartInfo.setIsChecked((existCartInfo.getIsChecked() == 1 || cartInfoDTO.getIsChecked() == 1) ? 1 : 0);

                //添加键值对。如果键存在，是更新数据
                map.put(skuId, existCartInfo);

            } else {                      //没有
                map.put(skuId, cartInfoDTO);
            }

        }
        // 4. 返回合并之后的集合
        return map.values().stream().collect(Collectors.toList());
    }

    /**
     * 更新选中状态
     */
    @Override
    public void checkCart(String userId, Integer isChecked, Long skuId) {

        //1. 从redis中获取商品对象
        RMap<Long, CartInfoDTO> map = redissonClient.getMap(buildKey(userId));
        CartInfoDTO cartInfoDTO = map.get(skuId);

        //2. 修改选中的状态
        cartInfoDTO.setIsChecked(isChecked);

        //3. 把修改之后的对象存入redis
        map.put(skuId, cartInfoDTO);
    }

    /**
     * @param skuId         商品id
     * @param userId        用户id
     * @return: void
     * 功能描述: 删除购物车指定的商品
     */
    @Override
    public void deleteCart(Long skuId, String userId) {
        //1. 获取用户的购物车对应的map
        RMap<Long, CartInfoDTO> map = redissonClient.getMap(buildKey(userId));
        //2. 删除对应的商品map的value
        map.remove(skuId);
    }

    /**
     * @param userId
     * @return: void
     * 功能描述: 删除购物车中所有选中的商品
     */
    @Override
    public void deleteChecked(String userId) {

        //1. 获取用户的购物车对应的map
        RMap<Long, CartInfoDTO> map = redissonClient.getMap(buildKey(userId));

        //2. 删除对应的商品map的value
        map.values().stream().forEach(cartInfoDTO -> {
            if (cartInfoDTO.getIsChecked() == 1) {
                map.remove(cartInfoDTO.getSkuId());
            }
        });
    }

    //获取购物车中被选中的商品列表
    @Override
    public List<CartInfoDTO> getCartCheckedList(String userId) {

        //1. 获取用户的购物车对应的map
        RMap<Long, CartInfoDTO> map = redissonClient.getMap(buildKey(userId));

        //2. 获取购物车中被选中的商品列表
        List<CartInfoDTO> checkedList = map.values().stream().map(cartInfoDTO -> {
            if (cartInfoDTO.getIsChecked() == 1) {
                return cartInfoDTO;
            }
            return null;
        }).filter(Objects::nonNull).collect(Collectors.toList());

        return checkedList;
    }

    //删除购物车中已经下单的商品
    @Override
    public void delete(String userId, List<Long> skuIds) {

        //1. 获取用户的购物车对应的map
        RMap<Long, CartInfoDTO> map = redissonClient.getMap(buildKey(userId));

        //2. 删除购物车中已经下单的商品
        map.values().stream().forEach(cartInfoDTO -> {
            if (skuIds.contains(cartInfoDTO.getSkuId())) {
                map.remove(cartInfoDTO.getSkuId());
            }
        });
    }


    @Override
    public void refreshCartPrice(String userId, Long skuId) {

        //1. 调用商品服务获取商品最新价格
        BigDecimal skuPrice = productApiClient.getSkuPrice(skuId);

        //2. 获取用户的购物车对应的map
        RMap<Long, CartInfoDTO> map = redissonClient.getMap(buildKey(userId));
        //3. 从redis中获取商品对象
        CartInfoDTO cartInfoDTO = map.get(skuId);
        //4. 修改商品对象的价格
        cartInfoDTO.setSkuPrice(skuPrice);
        //5. 把修改之后的对象存入redis
        map.put(skuId, cartInfoDTO);
    }
}
